/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org/scrmgr

    Copyright (C) 1991-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/
/*
 *    Copyright (C) 1986 Free Software Foundation, Inc.
 * 
 * This file is (derived from) part of GNU Emacs.
 * 
 * GNU Emacs is distributed in the hope that it will be useful,
 * but without any warranty.  No author or distributor
 * accepts responsibility to anyone for the consequences of using it
 * or for whether it serves any particular purpose or works at all,
 * unless he says so in writing.
 * 
 * Everyone is granted permission to copy, modify and redistribute
 * GNU Emacs, but only under the conditions described in the
 * document "GNU Emacs copying permission notice".   An exact copy
 * of the document is supposed to have been given to you along with
 * GNU Emacs so that you can know how you may redistribute it all.
 * It should be in a file named COPYING.  Among other things, the
 * copyright notice and this notice must be preserved on all copies.
 * 
 * Author: Jeff Peck, Sun Microsystems, Inc. <peck@sun.com>
 *
 * Original Idea: Ian Batten
 * Updated 15-Mar-88, Jeff Peck: set IN_EMACSTOOL, TERM, TERMCAP
 *
 * Wed Oct 21 1992: Hacked by Bob Hepple (BH) for other curses applications
 *
 * Some differences to the normal emacstool:
 *
 * 1. The Copy and Paste keys are trapped here and use the Sunview 
 *    selection (instead of passing them to the application - emacs
 *    is hard-coded to use the older SunWindows mechanism).
 *
 * 2. Down mouse button hits are sent to the application unless
 *    the user turns the 'Selection' on.
 *
 * 3. The frame is sized to 26x80.
 *
 * 4. This code only supports 'small' selection buffers - up to around 2K,
 *    or about 1 screens worth.
 *
 * 5. It's only tested on SunOS 4.1.1 and OpenWindows 3.0
 */

/* BH - You will need this in /etc/termcap:

cursestool|Application running under cursestool by Bob Hepple:\
	AL=\E[%p1%dL:DC=\E[%p1%dP:DL=\E[%p1%dM:IC=\E[%p1%d@:al=\E[L:\
	am:bl=^G:bs:cd=\E[J:ce=\E[K:cl=\f:cm=\E[%i%d;%dH:co#80:cr=\r:\
	dc=\E[P:dl=\E[M:do=\n:ei=:ic=\E[@:im=:\
	k1=\E*at:k2=\E*bt:k3=\E*ct:k4=\E*dt:k5=\E*et:\
	k6=\E*ft:k7=\E*gt:k8=\E*ht:k9=\E*it:\
	kD=^?:kH=\E*mr:kI=\E[247z:kN=\E*or:kP=\E*ir:\
	kd=\E*nr:kh=\E*gr:kl=\E*jr:km:kr=\E*lr:ku=\E*hr:\
	le=\b:li#25:md=\E[1m:me=\E[m:mi:mr=\E[7m:ms:nd=\E[C:\
	pt:rs=\E[s:se=\E[m:so=\E[7m:ta=\t:ue=\E[m:up=\E[A:us=\E[4m:

... and, this compiled into terminfo with tic: 
('untic' or /usr/5bin/infocmp can help with others ...)

cursestool|Application running under cursestool by Bob Hepple,
	am, km, msgr,
	cols#80, lines#25, 
# oddly - you have to get the lines# right!!
	acsc=k?lZm@jYnEwBvAuCt4qDx3, rev=\E[7m,
	bel=^G, bold=\E[1m, clear=\f, cr=\r, cub1=\b, cud1=\n,
	cuf1=\E[C, 
#	cup=\E[%i%p1%d;%p2%dH, 
	cuu1=\E[A,
	dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, dl1=\E[M,
	ed=\E[J, el=\E[K, home=\E[H, ht=\t, ich=\E[%p1%d@, ich1=\E[247z,
	il=\E[%p1%dL, il1=\E[L, ind=\n, kcub1=\E*jr,
	kcud1=\E*nr, kcuf1=\E*lr, kcuu1=\E*hr,
	kdch1=\E[249z, kf1=\E*at, kf2=\E*bt, kf3=\E*ct,
	kf4=\E*dt, kf5=\E*et, kf6=\E*ft, kf7=\E*gt,
	kf8=\E*ht, kf9=\E*it, khome=\E*gr, kll=\E*mr,
	knp=\E*or, kpp=\E*ir, kich1=\E[247z,
# Shifted keys...
	kLFT=\E*jR, kRIT=\E*lR, kEND=\E*mR, kBEG=\E*gR, kNXT=\E*oR, kPRV=\E*iR,
	rev=\E[7m, rmacs=\E[10m, rmcup=\E[>4h, rmso=\E[m,
	rmul=\E[m, rs2=\E[s,
#	sgr=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;m,
	sgr0=\E[m, smacs=\E[12m, smcup=\E[>4l, smso=\E[7m, smul=\E[4m,
 BH */

#include <xview/xview.h>
#include <xview/tty.h>
#include <xview/win_event.h>
#include <stdio.h>
#include <sys/file.h>
#include <xview/seln.h> /* BH */
#include <xview/font.h> /* BH */
#include <xview/panel.h> /* BH */
#include <xview/openmenu.h> /* BH */
#include <mylib.h>

char *xctool_rcsid(void) {
    return("$Id: xctool.c,v 1.7 2008/02/19 13:15:50 bhepple Exp $");
}

#define BUFFER_SIZE 128               	/* Size of all the buffers */

/* define WANT_CAPS_LOCK to make f-key T1 (aka F1) behave as CapsLock */
/* #define WANT_CAPS_LOCK */
#ifdef WANT_CAPS_LOCK
int caps_lock;		/* toggle indicater for f-key T1 caps lock */
static char *Caps = "[CAPS] ";		/* Caps Lock prefix string */
#define CAPS_LEN 7			/* strlen (Caps) */
#endif

static char *progname;

static char *mouse_prefix = "\030\000"; /* ^X^@ */
static int   mouse_prefix_length = 2;
static int   disable_mouse = 0; 	/* set to 1 to stop mouse events */
					/* going to the application */
static char *key_prefix = "\033*";  	/* ESC *   */
static int   k_prefix_length = 2;       /* key_prefix length */

static char *smdefault_name = "smproj";	/* default run command */
static char buffer[BUFFER_SIZE];	/* send to ttysw_input */
static char *title = "";		/* initial title */
static int  meta_flag = 0;

Frame 	frame;                          /* Base frame for system */
Tty 	ttysw;                          /* Where smcreate is */
Panel 	panel;
Menu	menu;
int font_width, font_height;            /* For translating pixels to chars */
Xv_opaque view;
static int selection_toggle = 0;

int console_fd = 0;			/* for debugging: setenv DEBUGCTOOL */
FILE *console = NULL;			/* for debugging: setenv DEBUGCTOOL */

/* typedef int 	(*f_ptr)(); */

#define META(x) (x)|0700
#define CTRL(x) (x)&037

Icon frame_icon;
/* make an icon_image for the default frame_icon */
static short default_image[258] = {
#include <images/terminal.icon>
};
mpr_static(icon_image, 64, 64, 1, default_image);

/*
 * Assign a value to a set of keys
 */
int button_value(event)
    Event *event;
{
    int retval = 0;
    
    /*
     * Code up the current situation:
     *
     * 1 = ACTION_SELECT;
     * 2 = ACTION_ADJUST;
     * 4 = ACTION_MENU;
     * 8 = SHIFT;
     * 16 = CONTROL;
     * 32 = META;
     * 64 = DOUBLE;
     * 128 = UP;
     */
#define BUTTON_MASK 7
    
    if ((event_action(event) == ACTION_SELECT) ||
	(event_id(event) == LOC_DRAG))
	retval = 1;
    else if (event_action(event) == ACTION_ADJUST) 
	retval = 2;
    else if (event_action(event) == ACTION_MENU) 
	retval = 4;
    
    if (event_shift_is_down (event)) retval += 8;
    if (event_ctrl_is_down  (event)) retval += 16;
    if (event_meta_is_down  (event)) retval += 32;
    if (event_is_up         (event)) retval += 128;
    return(retval);
}

/*
 *  Variables to store the time of the previous mouse event that was
 *  sent to smcreate.
 *
 *  The theory is that to time double clicks while ignoreing UP buttons,
 *  we must keep track of the accumulated time.
 *
 *  If someone writes a SUN-SET-INPUT-MASK for CTOOL,
 *  That could be used to selectively disable UP events, 
 *  and then this cruft wouldn't be necessary.
 */
static long prev_event_sec = 0;
static long prev_event_usec = 0;

/*
 *  Give the time difference in milliseconds, where one second
 *  is considered infinite.
 */
int time_delta(now_sec, now_usec, prev_sec, prev_usec)
    long now_sec, now_usec, prev_sec, prev_usec;
{
    long sec_delta = now_sec - prev_sec;
    long usec_delta = now_usec - prev_usec;
    
    if (usec_delta < 0) {		/* "borrow" a second */
	usec_delta += 1000000;
	--sec_delta;
    }
    
    if (sec_delta >= 10) 
	return(9999);		/* Infinity */
    else
	return(sec_delta * 1000 + usec_delta / 1000);
}

/*
 * Filter function to translate selected input events for smcreate
 * Mouse button events become ^X^@(button x-col y-line time-delta)^M
 * Function keys: ESC-*{c}{lrt} l,r,t for Left, Right, Top; 
 * {c} encodes the keynumber as a character [a-o]
 */
static Notify_value input_event_filter_function(window, event, arg, type)
    Window 		window;
    Event 		*event;
    Notify_arg 		arg;
    Notify_event_type 	type;
{
    struct timeval 	time_stamp;

    if (console_fd) {
	if (event_ctrl_is_down(event))
	    fputs("C-", console);
	if (event_shift_is_down(event))
	    fputs("S-", console);
	if (event_meta_is_down(event))
	    fputs("M-", console);
	if (event_is_ascii(event))
	    fputc(event_id(event), console);
	if (event_is_button(event))
	    fputs("MOUSE ", console);
	if (event_action(event) == ACTION_SELECT)
	    fputs("ACTION_SELECT ", console);
	if (event_action(event) == ACTION_ADJUST)
	    fputs("ACTION_ADJUST ", console);
	if (event_action(event) == ACTION_MENU)
	    fputs("ACTION_MENU ", console);
	if (event_id(event) == LOC_DRAG)
	    fputs("LOC_DRAG ", console);
	if (event_is_up(event))
	    fputs("UP ", console);

	fprintf(console, "Event: %d\n", event_id(event));
    }

    if (event_id(event) == WIN_REPAINT) {
	sprintf(buffer, "%c", 12); /* ^L */
	ttysw_input(ttysw, buffer, 1);
	return(NOTIFY_DONE);
    }

    /* 
      Note: curses on Sun does not appear to be able to cope with endwin()
      followed by initscr() (in any case, all the manuals says don't do it!)
      The only effect appears to be that stdin becomes no-wait and always 
      returns 0 but that's bad enough!
      Hence there is no clean way under curses to implement a resize 
      operation by restarting with initscr to pick up the new LINES &
      COLS variables.
    */

    /* do Mouse Button events */

    if (event_is_button(event) || (event_id(event) == LOC_DRAG))
	if (!disable_mouse && !selection_toggle) {
	    time_stamp = event_time(event);
	    ttysw_input(ttysw, mouse_prefix, mouse_prefix_length);
	    sprintf(buffer, "(%d %d %d %d)\015",
		    button_value(event),
		    event_x(event) / font_width,
		    event_y(event) / font_height,
		    time_delta(time_stamp.tv_sec, time_stamp.tv_usec,
			       prev_event_sec, prev_event_usec));
	    ttysw_input(ttysw, buffer, strlen(buffer));
	    prev_event_sec = time_stamp.tv_sec;
	    prev_event_usec = time_stamp.tv_usec;
	    return(NOTIFY_DONE);
	} else
	    return(notify_next_event_func(window, event, arg, type));
    
    /* We don't trap other up events */
    if (event_is_up(event)) 
	return(notify_next_event_func(window, event, arg, type));
    
    /* L1 is the STOP key */
    if (event_id(event) == WIN_STOP) {
	ttysw_input(ttysw, "\007\007\007\007\007\007\007", 7);
	return(NOTIFY_DONE);
    }
    
    /* L5,L6,L7,L8 are Expose, Copy, Open & Paste, let them pass to xview */
    if (event_id(event) == KEY_LEFT(5) ||
	event_id(event) == KEY_LEFT(6) ||
	event_id(event) == KEY_LEFT(7) ||
	event_id(event) == KEY_LEFT(8))
	return(notify_next_event_func(window, event, arg, type));
    
    { /* Do the other function key events */
	int	d;
	char 	c = (char) 0;
	
	if ((event_is_key_left(event)) ?
	    ((d = event_id(event) - KEY_LEFT(1)   + 'a'), c='l') : 
	    ((event_is_key_right(event)) ?
	     ((d = event_id(event) - KEY_RIGHT(1) + 'a'), c='r') : 
	     ((event_is_key_top(event)) ?
	      ((d = event_id(event) - KEY_TOP(1)  + 'a'), c='t') : 0))) {
	    
	    if (event_shift_is_down(event)) 
		c = c -  32;
	    
	    /* this will give a non-{lrt} for unshifted keys */
	    if (event_ctrl_is_down(event)) 
		c = c -  64;
	    if (event_meta_is_down(event)) 
		c = c + 128;
	    
#ifdef WANT_CAPS_LOCK
	    /* set a toggle and relabel window so T1 can act like caps-lock */
	    if (event_id(event) == KEY_TOP(1)) {
		/* make a frame label with and without CAPS */
		strcpy(buffer, Caps); 
		title = &buffer[CAPS_LEN];
		strncpy(title, (char *)xv_get(frame, XV_LABEL),
			BUFFER_SIZE - CAPS_LEN);
		buffer[BUFFER_SIZE] = (char) 0;	
		if (strncmp(title, Caps, CAPS_LEN) == 0)
		    title += CAPS_LEN; 		 /* already Caps */
		caps_lock = (caps_lock ? 0 : CAPS_LEN);
		xv_set(frame, XV_LABEL, (title -= caps_lock), 0);
		return(NOTIFY_DONE);
	    }
#endif
	    ttysw_input(ttysw, key_prefix, k_prefix_length);
	    sprintf(buffer, "%c%c", d, c);
	    ttysw_input(ttysw, buffer, strlen(buffer));
	    
	    return(NOTIFY_DONE);
	}
    }
    
#ifdef WANT_CAPS_LOCK
    /* shift alpha chars to upper case if toggle is set */
    if ((caps_lock) && event_is_ascii(event)
	&& (event_id(event) >= 'a') && (event_id(event) <= 'z'))
	event_set_id(event, (event_id(event) - 32));
    /* crufty, but it works for now. is there an UPCASE(event)? */
#endif
    
    if (event_is_string(event)) {
	char *p = event_string(event);
	if (console_fd)
	    fprintf(console, "String : %s\n", p);
	ttysw_input(ttysw, p, strlen(p));
	return(NOTIFY_DONE);
    }

#if 0 
    event_is_Ascii seems to be broken for meta events 
    if (event_is_ascii(event))
#endif
    if (event_id(event) < 256) {
	buffer[0] = event_id(event);
	if (event_meta_is_down(event)) {
	    if (console_fd)
		fprintf(console, "Meta-0x%x\n", *buffer);
	    buffer[0] = META(buffer[0]);
	} else
	    if (console_fd)
		fprintf(console, "Ascii-0x%x\n", *buffer);
	    
	buffer[1] = 0;
	ttysw_input(ttysw, buffer, strlen(buffer));
	return(NOTIFY_DONE);
    }
    
    return(notify_next_event_func(window, event, arg, type));
}

void selection(item, value, event)
    Panel_item 	item;
    int		value;
    Event	*event;
{
    if (value)
	selection_toggle = 0;
    else
	selection_toggle = 1;
}

Notify_value destroy_func(client, status)
    Notify_client	client;
    Destroy_status	status;
{
    if (status == DESTROY_CHECKING) {
	int	pid = (int)xv_get(ttysw, TTY_PID);

	/* harmless signal, just to see if the sucker's still there */
	if ((pid > 0) && (kill(pid, 19) == 0)) {
            ttysw_input(ttysw, META('Z'), 1); /* Meta-Z */
	    notify_veto_destroy(client);
	}
    } else if (status == DESTROY_CLEANUP)
	return(notify_next_destroy_func(client, status));
    return(NOTIFY_DONE);
}

int button_proc(item, event)
    Panel_item	item;
    Event	*event;
{
    /* This just notifies us of the key going down - the default action
       is notified in menu_proc. This is really an opportunity to alter
       the menu before it is displayed. */
}

#define MAX_MENUS	10
#define MAX_MENU_ITEMS 	20
#define BUTTON_DATA 	218

struct button_item {
    char 		*label;
    char		*value;
    struct button_list 	*popup;
};

struct button_list {
    char		*menu_name;
    int			num_items;
    Menu		menu;
    f_ptr		menu_proc;
    f_ptr		button_proc;
    struct button_item 	item[MAX_MENU_ITEMS];
};

struct button_info {
    int 		num_menus;
    int			num_lines_in_panel;
    struct button_list	**blroot;
};

char *read_line(buf, file)
{
    char *p, *s;
    
    while ((p = fgets(buf, 100, file)) != NULL) {
	while (*p && isspace(*p))
	    p++;
	s = strchr(p, '\n');
	if (s)
	    *s = 0;
	if ((*p != 0) && (*p != '#'))
	    break;
    }
    return(p);
}

/* Look for a space in a word */
char *parse_word(pos)
    char *pos;
{
    while (*pos && !isspace(*pos))
	pos++;
    *pos = 0;
    return(pos);
}

void safe_strcpy(d, s) 
    char	*d, *s;
{
    while (*d++ = *s++);
}

/* Look for ':' in a string */
char *parse_string(pos)
    char *pos;
{
    int		escape = 0;

    while (*pos) {
	if (*pos == '\\')
	    if (escape) {
		escape = 0;
		safe_strcpy(pos, pos + 1);
	    } else {
		escape = 1;
		safe_strcpy(pos, pos + 1);
	    }
	else if (*pos == ':')
	    if (escape) {
		escape = 0;
	    } else
		break;
	pos++;
    }
    if (*pos) {
	*pos = 0;
	return(pos + 1);
    } else
	return(NULL); /* no ':' */
}

char *skip_blanks(pos)
    char *pos;
{
    while (*pos && isspace(*pos))
	pos++;
    return(pos);
}

void process_ctrl_meta(word_start)
    char *word_start;
{
    char 	*p;
    int		escape = 0;
    
    /* remove \ and C- M- from the string */
    for (p = word_start; *p; p++) {
	if (*p == '\\')
	    if (escape) {
		safe_strcpy(p, p + 1);
		escape = 0;
	    } else
		escape = 1;
	else if (((toupper(*p) == 'C') || (toupper(*p) == 'M')) &&
		 (*(p + 1) == '-')) {
	    if (escape) {
		safe_strcpy(p - 1, p);
		escape = 0;
	    } else {
		if (*p == 'C')
		    *p = 1;
		else
		    *p = 2;
		safe_strcpy(p + 1, p + 2);
	    }
	} else if (escape) {
	    safe_strcpy(p - 1, p);
	    escape = 0;
	}
    }
    
    /* Now do the CTRL & META of characters */
    for (p = word_start; *p; p++) {
	char *s;
	int  ctrl, meta;
	
	ctrl = 0;
	meta = 0;
	for (s = p; (*s == 1) || (*s == 2); s++) {
	    if (*s == 1)
		ctrl = 1;
	    else
		meta = 1;
	}
	if (*s && (s > p)) {
	    *p = *s;
	    if (ctrl)
		*p = CTRL(*p);
	    if (meta)
		*p = META(*p);
	    safe_strcpy(p + 1, s + 1);
	}
    }
}

struct button_info *read_button_file(file)
    FILE	*file;
{
    char		buf[100], *word_start, *word_end;
    struct button_info	*bi;
    struct button_list 	**bl, **blroot;
    struct button_item	*item;
    int			i, meta, ctrl;

    bi = (struct button_info *) malloc(sizeof(struct button_info));
    bi->num_menus = 0;
    bi->num_lines_in_panel = 6; /* default */

    bi->blroot = (struct button_list **) malloc(sizeof(struct button_list *) * 
						MAX_MENUS);
    bl = bi->blroot - 1;

    for (i = 0; i < MAX_MENUS; i++)
	bi->blroot[i] = NULL;

    *(bi->blroot) = NULL;

    while ((word_start = read_line(buf, file)) != NULL) {
	word_end = parse_word(word_start);

	if (strcmp(word_start, "MENU") == 0) {
	    /* Start a new menu */
	    bl++;
	    bi->num_menus++;
	    if (bi->num_menus >= MAX_MENUS) {
		fprintf(stderr, "%s: Too many menus (max = %d)\n",
			progname, MAX_MENUS);
		exit(1);
	    }
	    word_start = skip_blanks(word_end + 1);
	    word_end = parse_word(word_start);

	    *bl = (struct button_list *) malloc(sizeof(struct button_list));
	    (*bl)->menu_name = malloc(strlen(word_start) + 1);
	    strcpy((*bl)->menu_name, word_start);
	    (*bl)->num_items = 0;
	    (*bl)->menu = NULL;
	    (*bl)->menu_proc = NULL;
	    (*bl)->button_proc = NULL;

	} else if (strcmp(word_start, "PANEL_LINES") == 0) {
	    i = atoi(word_end + 1);
	    if ((i < 2) || (i > 50)) {
		fprintf(stderr, "%s: PANEL_LINES is unreasonable (at %d)\n", 
			progname, i);
		exit(1);
	    }
	    bi->num_lines_in_panel = i;

	} else if (strcmp(word_start, "GLOBAL_QUIT") == 0) {
	} else if (strcmp(word_start, "MOUSE_OFF") == 0) {
	    disable_mouse = 1;
	} else if (strcmp(word_start, "MODE_CLUE") == 0) {
	} else {
	    /* Add item to *bl */
	    if (bl < bi->blroot) {
		fprintf(stderr, "%s: Menu file has no MENU line\n", progname);
		exit(1);
	    }
	    (*bl)->num_items++;
	    if ((*bl)->num_items >= MAX_MENU_ITEMS) {
		fprintf(stderr, 
			"%s: Too many menu items under \"%s\" (max = %d)\n",
			progname, (*bl)->menu_name, MAX_MENU_ITEMS);
		exit(1);
	    }

	    item = &((*bl)->item[(*bl)->num_items - 1]);

	    *word_end = ' '; /* repair line after parse_word */
	    word_end = parse_string(word_start);
	    if (!word_end) {
		fprintf(stderr, 
			"%s: Button label should be terminated with ':' in %s\n", 
			progname, word_start);
		exit(1);
	    }

	    item->label = malloc(strlen(word_start) + 1);
	    strcpy(item->label, word_start);
	    word_start = skip_blanks(word_end + 1);
	    if (*word_start == 0) {
		fprintf(stderr, 
			"%s, Menu item \"%s\" has no key stroke\n", 
			progname, item->label);
		exit(1);
	    }

	    
	    item->value = NULL;
	    item->popup = NULL;
	    
	    if (*word_start == '>') {
		word_start++;
		/* it's a popup button */
		for (i = 0; i < bi->num_menus - 1; i++)
		    if (strcmp(word_start, bi->blroot[i]->menu_name) == 0)
			item->popup = bi->blroot[i];
		if (item->popup == NULL) {
		    fprintf(stderr, "%s: No such menu: %s\n", 
			    progname, word_start);
		    exit(1);
		}
	    } else {
		/* it's a regular button - process any C- or M- chars but
		   leave \C- and \M- alone	*/
		process_ctrl_meta(word_start);
		
		item->value = malloc(strlen(word_start) + 1);
		strcpy(item->value, word_start);
	    }
	}
    }

    return(bi);
}

void menu_proc(menu, menu_item)
    Menu 	menu;
    Menu_item	menu_item;
{
    char *c = (char *)xv_get(menu_item, XV_KEY_DATA, BUTTON_DATA);
    
    ttysw_input(ttysw, c, strlen(c));
}

void button_notify(item, event)
    Panel_item	item;
    Event	*event;
{
    char *c = (char *)xv_get(item, XV_KEY_DATA, BUTTON_DATA);
    
    ttysw_input(ttysw, c, strlen(c));
}

void populate_panel(panel, blroot, blMain)
    Panel		panel;
    struct button_list 	**blroot, *blMain;
{
    int j;

    for (j = 0; j < blMain->num_items; j++) {
	struct button_item *item = &blMain->item[j];
	
	if (item->popup) {
	    int			k;
	    struct button_list	*bl = item->popup;
	    
	    bl->menu = 
		(Menu)xv_create(NULL, MENU,
				MENU_NOTIFY_PROC, menu_proc,
				NULL);
    	    xv_create(panel, PANEL_BUTTON,
		      PANEL_NOTIFY_PROC, button_proc,
		      PANEL_LABEL_STRING, item->label,
		      PANEL_ITEM_MENU, bl->menu,
		      NULL);
	    for (k = 0; k < bl->num_items; k++)
		xv_set(bl->menu, MENU_ITEM,
		       XV_KEY_DATA, BUTTON_DATA, bl->item[k].value,
		       MENU_STRING, bl->item[k].label,
		       NULL);
	} else
	    xv_create(panel, PANEL_BUTTON,
		      PANEL_NOTIFY_PROC, button_notify,
		      PANEL_LABEL_STRING, item->label,
		      XV_KEY_DATA, BUTTON_DATA, item->value,
		      NULL);
    }
    
    xv_create(panel, PANEL_CHOICE,
	      PANEL_NOTIFY_PROC, selection,
	      PANEL_LABEL_STRING, "Selection",
	      PANEL_CHOICE_STRINGS, "On", "Off", NULL,
	      PANEL_VALUE, 1,
/*	      PANEL_NEXT_ROW, -1,*/
	      NULL);
}

main(argc, argv)
    int argc;
    char **argv;
{
    int 		error_code;	/* Error codes */
    Xv_font		font;
    Xv_font		original_font;
    int			i, j;
    struct button_info	*bi;
    struct button_list	**blroot, *blMain;
    char		*menu_filename;
    FILE		*f;

    xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);
    
    progname = argv[0];

    if (getenv("DEBUGCTOOL")) {
	console = fdopen(console_fd = open("/dev/console", O_WRONLY), "w");
	fprintf(console, "Debugging %s\n", progname);
    }
    
    putenv("IN_CTOOL=t");	/* notify subprocess that it is in CTOOL */
    
    if (putenv("TERM=cursestool") != 0)	{
	/* TTYSW will be a TERM=cursestool window */
	fprintf(stderr, "%s: Could not set TERM=cursestool, using `%s'\n",
		 progname, (char *)getenv("TERM")) ;
    }
    
    /*
     * If TERMCAP starts with a slash, it is the pathname of the
     * termcap file, not an entry extracted from it, so KEEP it!
     * Otherwise, it may not relate to the new TERM, so Nuke-It.
     * If there is no TERMCAP environment variable, don't make one.
     */
    {
	char *termcap ;	/* Current TERMCAP value */
	
	termcap = (char *)getenv("TERMCAP") ;
	if (termcap && (*termcap != '/')) {
	    if (putenv("TERMCAP=") != 0) {
		fprintf(stderr, "%s: Could not clear TERMCAP\n", progname) ;
	    }
	}
    }
    
    /* find command to run as subprocess in window */
    if (!(argv[0] = (char *)getenv("CTOOL")))	/* Set smcreate command name */
	argv[0] = smdefault_name;			
    for (argc = 1; argv[argc]; argc++)		/* Use last one on line */
	if(!(strcmp("-rc", argv[argc]))) {	/* Override if -rc given */
	    int i = argc;
	    argv[argc--]=0;	/* kill the -rc argument */
	    if (argv[i+1]) {    /* move to argv[0] and squeeze the rest */
		argv[0]=argv[i+1];
		for (; argv[i+2]; (argv[i]=argv[i+2],argv[++i]=0));
	    }
	}
    
    /* find menu file to run as subprocess in window */
    menu_filename = NULL;
    for (argc = 1; argv[argc]; argc++)		/* Use last one on line */
	if(!(strcmp("-f", argv[argc]))) {	/* Override if -rc given */
	    int i = argc;
	    argv[argc--]=0;	/* kill the -f argument */
	    if (argv[i + 1]) {    /* move to argv[0] and squeeze the rest */
		menu_filename = argv[i + 1];
		for (; argv[i + 2]; (argv[i]=argv[i + 2],argv[++i]=0));
	    }
	}

    if (menu_filename == NULL)
	menu_filename = "scrmgr.xct";


    if ((f = fopen(menu_filename, "r")) == NULL) {
	if ((f = pathopen(menu_filename, "r", NULL)) == NULL) {
	    fprintf(stderr, "%s: Can't open menu file: %s\n", 
		    progname, menu_filename);
	    exit(1);
	}
    }

    bi = read_button_file(f);
    blroot = bi->blroot;
    fclose(f);

    blMain = NULL;
    for (i = 0; i < bi->num_menus; i++)
	if (strcmp(blroot[i]->menu_name, "MAIN") == 0)
	    blMain = blroot[i];

    if (blMain == NULL) {
	fprintf(stderr, "%s: No MAIN menu defined\n", progname);
	exit(1);
    }

    strcpy(buffer, title);
    strncat(buffer, argv[0],		 /* append run command name */
	    (BUFFER_SIZE - (strlen(buffer)) - (strlen(argv[0]))) - 1);
    
    frame_icon = icon_create(XV_LABEL, argv[0],
			     ICON_IMAGE, &icon_image,
			     0);
    
    /* Build a frame to run in */
    frame = xv_create ((Window)NULL, FRAME,
		       XV_LABEL,		buffer,
		       FRAME_ICON,		frame_icon,
		       FRAME_SHOW_RESIZE_CORNER, FALSE,
		       0);
    {
	/* Patch for Sun 4.1.1/OW3.0 - the Insert key is munged.
	   While we're at it we might as well do the = / & *
	   keys too.
	   */
#include "X11/keysym.h"
	Display *dpy = (Display *)xv_get(frame, XV_DISPLAY);
	XRebindKeysym(dpy, XK_Insert, 0, 0, "\033[247z", 6);
	 /* These don't work since event_String only looks at strings 
	    longer than 1 character!! Dumb!!!! */
	XRebindKeysym(dpy, XK_F24, 0, 0, "=", 1);
	XRebindKeysym(dpy, XK_F25, 0, 0, "/", 1);
	XRebindKeysym(dpy, XK_F26, 0, 0, "*", 1);
    }
	
/* This does not work for width with the default font. Therefore use
   courier which does work. Fixed also has problems.
    font_height = (int) xv_get(ttysw, WIN_ROW_HEIGHT) +
	(int) xv_get(ttysw, WIN_ROW_GAP);
    font_width  = (int) xv_get(ttysw, WIN_COLUMN_WIDTH) +
	(int) xv_get(ttysw, WIN_COLUMN_GAP);
*/
    original_font = (Xv_Font) xv_get(frame, XV_FONT);

    if ((font = (Xv_Font) xv_find(frame, FONT, FONT_NAME, "courier", NULL)) 
	!= NULL)
	xv_set(frame, XV_FONT, font, NULL);
    font = (Xv_font) xv_get(frame, XV_FONT);
    font_width = (int) xv_get(font, FONT_DEFAULT_CHAR_WIDTH);
    font_height = (int) xv_get(font, FONT_DEFAULT_CHAR_HEIGHT);

    panel = xv_create(frame, PANEL, 
		      XV_FONT, original_font,
		      XV_Y, 0,
		      XV_X, 0,
		      XV_WIDTH, font_width * 80,
		      XV_HEIGHT, font_height * bi->num_lines_in_panel,
		      WIN_BORDER, FALSE,
		      NULL);
    populate_panel(panel, blroot, blMain);

    /* Create a tty with SMDEFAULT_NAME in it */
    ttysw = xv_create(frame, TTY, 
		      WIN_BELOW, panel,
		      XV_X, 0,
		      /*WIN_ROWS, 26,
		      WIN_COLUMNS, 80,*/
		      TTY_BOLDSTYLE, 8, 
		      TTY_QUIT_ON_CHILD_DEATH, TRUE, 
		      TTY_ARGV, argv, 
		      0);
    view = xv_get(ttysw, OPENWIN_NTH_VIEW, NULL);

    /* xv_set(panel, XV_WIDTH, (int)xv_get(ttysw, XV_WIDTH), NULL); */

    frame_fit_all(frame);

    /* Interpose my event function */
    error_code = (int)  notify_interpose_event_func 
	(view, input_event_filter_function, NOTIFY_SAFE);
    
    if (error_code != 0) {                      /* Barf */
	fprintf(stderr, "%s: notify_interpose_event_func got %d.\n", 
		progname, error_code);
	exit(1);
    }

    /* For the Quit button and other attempts to kill me ... */
    notify_interpose_destroy_func(frame, destroy_func);

    window_main_loop(frame);                  /* And away we go */
}

/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
